package info.batcloud.fanli.core.service.impl;

import info.batcloud.fanli.core.dto.ShopcatDTO;
import info.batcloud.fanli.core.constants.CacheNameConstants;
import info.batcloud.fanli.core.entity.OutCatToShopcat;
import info.batcloud.fanli.core.entity.Shopcat;
import info.batcloud.fanli.core.enums.ItemSelectionPlat;
import info.batcloud.fanli.core.enums.ShopcatStatus;
import info.batcloud.fanli.core.exception.BizException;
import info.batcloud.fanli.core.exception.ShopcatNameExistsException;
import info.batcloud.fanli.core.helper.OSSImageHelper;
import info.batcloud.fanli.core.repository.OutCatToShopcatRepository;
import info.batcloud.fanli.core.repository.ShopcatRepository;
import info.batcloud.fanli.core.service.ShopcatService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.inject.Inject;
import javax.transaction.Transactional;
import java.util.*;
import java.util.stream.Collectors;

@Service
@CacheConfig(cacheNames = CacheNameConstants.SHOPCAT)
public class ShopcatServiceImpl implements ShopcatService {

    @Inject
    private ShopcatRepository shopcatRepository;

    @Inject
    private ShopcatService shopcatService;

    @Inject
    private OutCatToShopcatRepository outCatToShopcatRepository;

    @Override
    @CacheEvict(cacheNames = CacheNameConstants.SHOPCAT, allEntries = true)
    public void saveShopcat(Shopcat shopcat) throws ShopcatNameExistsException {
        if (StringUtils.isBlank(shopcat.getKeyword())) {
            shopcat.setKeyword(shopcat.getTitle());
        }
        if (shopcatRepository.countByParentIdAndTitle(shopcat.getParentId(), shopcat.getTitle()) > 0) {
            throw new ShopcatNameExistsException();
        }
        shopcatRepository.save(shopcat);
        Shopcat parent = shopcatRepository.findOne(shopcat.getParentId());
        if (parent != null) {
            shopcat.setPath(parent.getPath() + shopcat.getId().toString() + "/");
        } else {
            shopcat.setPath(shopcat.getId().toString() + "/");
        }
        shopcatRepository.save(shopcat);
    }

    @Override
    @CacheEvict(cacheNames = CacheNameConstants.SHOPCAT, allEntries = true)
    public void updateShopcat(Shopcat shopcat) throws ShopcatNameExistsException {
        Shopcat eShopcat = shopcatRepository.findOne(shopcat.getId());
        if (shopcatRepository.countByParentIdAndTitleAndIdNotAndStatusNot(eShopcat.getParentId(), shopcat.getTitle(), shopcat.getId(),
                ShopcatStatus.DELETED) > 0) {
            throw new ShopcatNameExistsException();
        }
        if(shopcat.getRef() != null) {
            if(shopcat.getRef().getId().equals(shopcat.getId())) {
                throw new BizException("不能引用自己");
            }
        }
        eShopcat.setStatus(shopcat.getStatus());
        eShopcat.setKeyword(StringUtils.isBlank(shopcat.getKeyword()) ? shopcat.getTitle() : shopcat.getKeyword());
        eShopcat.setTitle(shopcat.getTitle());
        eShopcat.setSearchByKeyword(shopcat.isSearchByKeyword());
        eShopcat.setRef(shopcat.getRef());
        Shopcat parent = shopcatRepository.findOne(eShopcat.getParentId());
        if (parent != null) {
            eShopcat.setPath(parent.getPath() + shopcat.getId().toString() + "/");
        } else {
            eShopcat.setPath(shopcat.getId().toString() + "/");
        }
        shopcatRepository.save(eShopcat);
    }

    @Override
    @Cacheable(key = "#id + ''")
    public ShopcatDTO findById(long id) {
        return of(shopcatRepository.findOne(id));
    }

    @Override
    public ShopcatDTO findRealById(long id) {
        return findReal(findById(id));
    }

    @Override
    @Transactional
    @CacheEvict(cacheNames = CacheNameConstants.SHOPCAT, allEntries = true)
    public void deleteById(long id) {
        Shopcat shopcat = shopcatRepository.findOne(id);
        List<Shopcat> children = shopcatRepository.findByPathStartingWith(shopcat.getId().toString());
        children.forEach(o -> {
            o.setStatus(ShopcatStatus.DELETED);
            shopcatRepository.save(o);
        });
        shopcat.setStatus(ShopcatStatus.DELETED);
        shopcatRepository.save(shopcat);
    }

    @Override
    public String getPathName(String path) {
        StringBuffer sb = new StringBuffer();
        for (String s : path.split("/")) {
            sb.append(shopcatService.findById(Long.valueOf(s)).getTitle());
            sb.append("/");
        }
        return sb.toString();
    }

    @Override
    public List<ShopcatDTO> findEnabledByParentId(long parentId) {
        return this.findEnabledByParentIdList(Arrays.asList(parentId));
    }

    @Override
    public List<ShopcatDTO> findValidNoIconList() {
        return this.shopcatRepository.findByIconIsNullAndStatusNot(ShopcatStatus.DELETED)
                .stream().map(o -> of(o)).collect(Collectors.toList());
    }

    @Override
    public List<ShopcatDTO> findEnabledByParentIdList(List<Long> parentIdList) {
        List<Shopcat> shopcats = shopcatRepository.findByParentIdInAndStatusOrderByIdxAsc(parentIdList, ShopcatStatus.ENABLED);
        List<ShopcatDTO> list = new ArrayList<>();
        for (Shopcat shopcat : shopcats) {
            ShopcatDTO vo = new ShopcatDTO();
            vo.setIconUrl(shopcat.getIconUrl());
            vo.setId(shopcat.getId());
            vo.setTitle(shopcat.getTitle());
            vo.setKeyword(shopcat.getKeyword());
            vo.setPath(shopcat.getPath());
            vo.setParentId(shopcat.getParentId());
            vo.setIdx(shopcat.getIdx());
            if (shopcat.getRef() != null) {
                vo.setRef(of(shopcat.getRef()));
            }
            list.add(vo);
        }
        return list;
    }

    @Override
    @Cacheable(key = "'SHOPCAT_CHILDREN_BY_PARENT_' + #parentIds")
    public List<ShopcatDTO> findEnabledByParentIds(String parentIds) {
        return this.findEnabledByParentIdList(Arrays.stream(parentIds.split(","))
                .map( o -> Long.valueOf(o)).collect(Collectors.toList()));
    }

    @Override
    @Cacheable(key = "'SHOPCAT_TREE_PATH_' + #path")
    public List<ShopcatDTO> findEnabledTreeByPath(String path) {
        if (!path.endsWith("/")) {
            path += "/";
        }
        List<Shopcat> list = shopcatRepository.findByPathStartingWithAndStatusOrderByIdAsc(path, ShopcatStatus.ENABLED);
        List<ShopcatDTO> rootList = new ArrayList<>();
        Map<Long, ShopcatDTO> map = new HashMap<>();
        for (Shopcat shopcat : list) {
            ShopcatDTO vo = of(shopcat);
            vo.setChildren(new ArrayList<>());
            map.put(shopcat.getId(), vo);
            if (shopcat.getPath().equals(path)) {
                rootList.add(vo);
            } else {
                ShopcatDTO parent = map.get(shopcat.getParentId());
                if (parent == null) {
                    continue;
                }
                parent.getChildren().add(vo);
            }
        }
        Collections.sort(rootList, Comparator.comparingInt(ShopcatDTO::getIdx));
        //处理排序问题
        for (ShopcatDTO shopcatDTO : map.values()) {
            Collections.sort(shopcatDTO.getChildren(), Comparator.comparingInt(ShopcatDTO::getIdx));
        }
        return rootList.get(0).getChildren();
    }

    @Override
    public Set<Long> findShopcatIdByTaobaoCatId(long taobaoCatId) {
        List<OutCatToShopcat> outCatToShopcats = outCatToShopcatRepository.findByPlatAndCatId(ItemSelectionPlat.TAOBAO, taobaoCatId);
        return outCatToShopcats.stream().map(c -> c.getShopcatId()).collect(Collectors.toSet());
    }

    @Override
    public ShopcatDTO findReal(ShopcatDTO shopcatDTO) {
        return shopcatDTO.getRef() == null ? shopcatDTO : findReal(shopcatDTO.getRef());
    }

    @Override
    public Set<Long> findShopcatIdByJdCatId(long jdCatId) {
        List<OutCatToShopcat> outCatToShopcats = outCatToShopcatRepository.findByPlatAndCatId(ItemSelectionPlat.JD, jdCatId);
        return outCatToShopcats.stream().map(c -> c.getShopcatId()).collect(Collectors.toSet());
    }

    @Override
    public List<ShopcatDTO> findValidByParentId(long parentId) {
        List<Shopcat> shopcats = shopcatRepository.findByParentIdAndStatusNotOrderByIdxAsc(parentId, ShopcatStatus.DELETED);
        return shopcats.stream().map(s -> of(s)).collect(Collectors.toList());
    }

    @Override
    public List<ShopcatDTO> findValidByTitleLike(String title) {
        List<Shopcat> shopcats = shopcatRepository.findByTitleLikeAndStatusNotOrderByIdxAsc("%" + title + "%", ShopcatStatus.DELETED);
        return shopcats.stream().map(s -> of(s)).collect(Collectors.toList());
    }

    private static ShopcatDTO of(Shopcat shopcat) {
        ShopcatDTO dto = new ShopcatDTO();
        BeanUtils.copyProperties(shopcat, dto);
        dto.setIconUrl(OSSImageHelper.toSmallUrl(shopcat.getIconUrl()));
        if(shopcat.getRef() != null) {
            dto.setRef(of(shopcat.getRef()));
        }
        return dto;
    }
}
